package org.example.util;

import org.example.bean.LazyNode;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName LazyNodePool
 * @Description TODO
 * @Author hongjun
 * @Date 2024/5/19 下午6:49
 * @Version 1.0
 */
public class LazyNodePool {
    // 使用线程安全的队列存储可用的节点
    private final Queue<LazyNode> availableNodes = new ConcurrentLinkedQueue<>();
    // 当前池中的节点数量（包括被借出和未被借出的）
    private final AtomicInteger currentPoolSize = new AtomicInteger(0);
    // 池的最大容量
    private final int maxPoolSize;
    // 当前已被借出的节点数量
    private final AtomicInteger acquiredNodeCount = new AtomicInteger(0);

    // 构造函数，初始化节点池
    public LazyNodePool(int initialSize, int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;

        // 初始化节点池，添加指定数量的节点
        for (int i = 0; i < initialSize; i++) {
            availableNodes.add(new LazyNode());
            currentPoolSize.incrementAndGet();
        }
    }

    // 从池中获取一个节点
    public LazyNode acquire() {
        // 尝试从队列中获取一个可用的节点
        LazyNode node = availableNodes.poll();
        // 如果没有可用节点且当前节点数未达到最大容量，则创建一个新节点
        if (node == null && currentPoolSize.get() < maxPoolSize) {
            synchronized (this) {
                // 再次检查以确保线程安全地创建新节点
                if (currentPoolSize.get() < maxPoolSize) {
                    node = new LazyNode();
                    currentPoolSize.incrementAndGet(); // 增加当前节点数量
                    acquiredNodeCount.incrementAndGet(); // 增加已获取节点数量
                }
            }
        } else if (node != null) {
            acquiredNodeCount.incrementAndGet(); // 如果获取到了已有节点，增加已获取节点数量
        }
        return node; // 返回节点，可能是新创建的，也可能是从队列中取出的
    }

    // 释放一个节点回池中
    public void release(LazyNode node) {
        if (node != null && acquiredNodeCount.get() > 0) {
            node.reset(); // 重置节点状态
            availableNodes.add(node); // 将节点放回到可用队列中
            acquiredNodeCount.decrementAndGet(); // 减少已获取节点数量
        }
    }

    // 获取当前已被借出的节点数量，用于监控和检测节点泄露
    public int getAcquiredNodeCount() {
        return acquiredNodeCount.get();
    }
}
